﻿Option Strict On
Option Explicit On

''' <summary>
''' Provides the basic container for all game elements.  Each game will define classes which inherit
''' from GameObject to create the various characters and objects in the game, as well as control objects
''' such as engine state machines.
''' </summary>
''' <remarks></remarks>
Public MustInherit Class GameObject
    Public Event Collision As EventHandler(Of CollisionEventArgs)
    Public Event Draw As EventHandler(Of DrawEventArgs)
    Public Event Input As EventHandler(Of GameEngineEventArgs)
    Public Event Load As EventHandler(Of GameEngineEventArgs)
    Public Event MouseDown As EventHandler(Of GameEngineEventArgs)
    Public Event MouseUp As EventHandler(Of GameEngineEventArgs)
    Public Event Update As EventHandler(Of GameEngineEventArgs)

    Protected _UniqueID As Guid
    ''' <summary>
    ''' Every GameObject can be uniquely identified by this value.
    ''' This value also serves as the object's default name.
    ''' </summary>
    ''' <value>A globally unique identifier.</value>
    ''' <returns>The globally unique identifier for this GameObject.</returns>
    ''' <remarks></remarks>
    Public ReadOnly Property UniqueID As Guid
        Get
            Return _UniqueID
        End Get
    End Property

    Private _CollisionPadding As Padding
    ''' <summary>
    ''' Specifies a specific bounding box for collision around this GameObject.
    ''' Setting this property causes the CollisionRadius to become the smaller of
    ''' either the width or the height of a rectangle bounding this padding divided by two.
    ''' </summary>
    ''' <value>A System.Windows.Forms.Padding object.</value>
    ''' <returns>The Padding representing the collision bounding for this GameObject.</returns>
    ''' <remarks></remarks>
    Public Property CollisionPadding As Padding
        Get
            Return _CollisionPadding
        End Get
        Set(ByVal value As Padding)
            _CollisionPadding = value
            _CollisionRadius = Math.Min(_CollisionPadding.Size.Width, _CollisionPadding.Size.Height) / 2.0F
        End Set
    End Property

    Private _CollisionRadius As Single
    ''' <summary>
    ''' Specifies the distance from the center of this GameObject at which collision will be detected.
    ''' This is the preferred method of specifing collision bounds. Setting this property causes the
    ''' CollisionPadding.All value to be set to the radius.
    ''' </summary>
    ''' <value>A System.Single containing the radius.</value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property CollisionRadius() As Single
        Get
            Return _CollisionRadius
        End Get
        Set(ByVal value As Single)
            _CollisionRadius = value
            _CollisionPadding.All = CInt(value)
        End Set
    End Property

    Protected _Data As DictionaryBank(Of String)
    ''' <summary>
    ''' Provides a named collection of various values which can be saved to disk and restored if needed.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property Data() As DictionaryBank(Of String)
        Get
            Return _Data
        End Get
    End Property

    Protected _Enabled As Boolean
    ''' <summary>
    ''' Determines if this GameObject is enabled within the game. Disabled GameObjects do
    ''' not get processed or drawn to the screen.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Enabled() As Boolean
        Get
            Return _Enabled
        End Get
        Set(ByVal value As Boolean)
            _Enabled = value
        End Set
    End Property

    Protected _EnableRaisingEvents As Boolean
    ''' <summary>
    ''' Specifies whether or not this GameObject raises local events which can be subscribed to.
    ''' Raising events adds additional overhead and should be used sparingly, and with fast-executing event handlers.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property EnableRaisingEvents() As Boolean
        Get
            Return _EnableRaisingEvents
        End Get
        Set(ByVal value As Boolean)
            _EnableRaisingEvents = value
        End Set
    End Property

    Private _HasCollision As Boolean
    ''' <summary>
    ''' Specifies whether or not this object responds to collision events.
    ''' Setting this property to True forces IsCollider to be True.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property HasCollision() As Boolean
        Get
            Return _HasCollision
        End Get
        Set(ByVal value As Boolean)
            _HasCollision = value
            If _HasCollision Then _IsCollider = True
        End Set
    End Property

    Private _IsCollider As Boolean
    ''' <summary>
    ''' Specifies whether or not this object causes collision.
    ''' Setting this property to False forces HasCollision to be False.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property IsCollider As Boolean
        Get
            Return _IsCollider
        End Get
        Set(ByVal value As Boolean)
            _IsCollider = value
            If Not value Then _HasCollision = False
        End Set
    End Property

    Protected _IsLoaded As Boolean
    ''' <summary>
    ''' Specifies whether or not the OnLoaded method has executed for this GameObject.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property IsLoaded As Boolean
        Get
            Return _IsLoaded
        End Get
    End Property

    Protected _Name As String
    ''' <summary>
    ''' The unique name of this GameObject. By default, this is the string representation of the GameObject.UniqueId value.  This name may be
    ''' changed to make referring to the GameObject easier. However, any GameObjectCollection will treat names as unique keys, so it is not
    ''' advisable to use the same name for multiple GameObjects.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Name() As String
        Get
            Return _Name
        End Get
        Set(ByVal value As String)
            _Name = value
        End Set
    End Property

    Protected _Position As Vector2
    ''' <summary>
    ''' The current position of this object.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Position() As Vector2
        Get
            Return _Position
        End Get
        Set(ByVal value As Vector2)
            _Position = value
        End Set
    End Property

    Protected _ProcessInput As Boolean
    ''' <summary>
    ''' Determines if this GameObject handles its OnInput method.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property ProcessInput() As Boolean
        Get
            Return _ProcessInput
        End Get
        Set(ByVal value As Boolean)
            _ProcessInput = value
        End Set
    End Property

    Protected _RotationAngle As Single
    Public Property RotationAngle() As Single
        Get
            Return _RotationAngle
        End Get
        Set(ByVal value As Single)
            _RotationAngle = value
        End Set
    End Property

    Protected _Scale As Single
    Public Property Scale() As Single
        Get
            Return _Scale
        End Get
        Set(ByVal value As Single)
            _Scale = value
        End Set
    End Property

    Protected _Sprites As SpriteCollection
    ''' <summary>
    ''' The collection of Sprite images to be drawn for this object.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property Sprites As SpriteCollection
        Get
            Return _Sprites
        End Get
    End Property

    Private _UseRectangleCollider As Boolean
    ''' <summary>
    ''' Determines if collision is calculated based on a bounding rectangle, or circle.  The circle collision is
    ''' the most efficient so this property is set to False by default.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property UseRectangleCollider() As Boolean
        Get
            Return _UseRectangleCollider
        End Get
        Set(ByVal value As Boolean)
            _UseRectangleCollider = value
        End Set
    End Property

    Protected _Visible As Boolean
    ''' <summary>
    ''' Determines if this GameObject is drawn to the screen. An invisible GameObject can still process input
    ''' and updates, but it will not receive a call to OnDraw.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Visible() As Boolean
        Get
            Return _Visible
        End Get
        Set(ByVal value As Boolean)
            _Visible = value
        End Set
    End Property

    Protected _ZOrder As Integer
    ''' <summary>
    ''' The order in which the object is drawn to the screen. Lower z-order objects are drawn first, with
    ''' higher z-order objects drawn on top. You may specificy negative values to push objects back.
    ''' Note that z-orders are calculated within the object's local container level; that is, all backgrounds
    ''' are sorted by z-order, and then objects are sorted by their z-order and drawn, with HUD elements sorted
    ''' and drawn last.  So a Background with ZOrder = 0 will always be under a HUD element with ZOrder = 0.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property ZOrder() As Integer
        Get
            Return _ZOrder
        End Get
        Set(ByVal value As Integer)
            _ZOrder = value
        End Set
    End Property

    Public Sub New()
        _UniqueID = Guid.NewGuid
        _Name = _UniqueID.ToString
        _Enabled = True
        _Data = New DictionaryBank(Of String)
        _Sprites = New SpriteCollection
        _Scale = Geometry.ONE
        _IsCollider = True
        _Visible = True
    End Sub

    ''' <summary>
    ''' Returns as Circle bounding this object at its current position.
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overridable Function GetCircleCollider() As Circle
        Return New Circle(_Position, _CollisionRadius * _Scale)
    End Function

    ''' <summary>
    ''' Returns a Rectangle bounding this object at its current position.
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overridable Function GetRectangleCollider() As RectangleF
        Return New RectangleF(_Position.X - _CollisionPadding.Left, _Position.Y - _CollisionPadding.Top, _CollisionPadding.Size.Width, _CollisionPadding.Size.Height)
    End Function

    ''' <summary>
    ''' This method is called by the GameEngine when this object collides with another GameObject.
    ''' </summary>
    ''' <param name="e">A GameEngineEventArgs instance containing a reference to the GameEngine and GameTime, as well as information about the collision.</param>
    ''' <remarks></remarks>
    Protected Friend Overridable Sub OnCollision(ByVal e As CollisionEventArgs)
        If _EnableRaisingEvents Then RaiseEvent Collision(Me, e)
    End Sub

    ''' <summary>
    ''' This method is called by the GameEngine once per frame when the object is drawn to the screen.
    ''' The default implementation draws each Sprite in this GameObject to the screen.
    ''' </summary>
    ''' <param name="e">A GameEngineEventArgs instance containing a reference to the GameEngine and GameTime, as well as the Graphics object to draw with.</param>
    ''' <remarks></remarks>
    Protected Friend Overridable Sub OnDraw(ByVal e As DrawEventArgs)
        If _Sprites.Count > 0 Then
            Dim spriteArgs As New SpriteDrawEventArgs(Me, e)
            Dim sprites() As Sprite = _Sprites.ToZOrderedArray
            For index As Integer = 0 To sprites.Length - 1
                sprites(index).OnDraw(spriteArgs)
            Next
        End If
        If _EnableRaisingEvents Then RaiseEvent Draw(Me, e)
    End Sub

    ''' <summary>
    ''' This method is called by the GameEngine the first time the object is accessed each frame, if the IsLoaded property equals false.
    ''' </summary>
    ''' <param name="e">A GameEngineEventArgs instance containing a reference to the GameEngine and GameTime.</param>
    ''' <remarks></remarks>
    Protected Friend Overridable Sub OnLoad(ByVal e As GameEngineEventArgs)
        If _IsLoaded Then Exit Sub
        If _EnableRaisingEvents Then RaiseEvent Load(Me, e)
        _IsLoaded = True
    End Sub

    ''' <summary>
    ''' This method is called by the GameEngine once per frame at the beginning of each engine iteration, if
    ''' the ProcessInput property is true on this object.
    ''' </summary>
    ''' <param name="e">A GameEngineEventArgs instance containing a reference to the GameEngine and GameTime.</param>
    ''' <remarks></remarks>
    Protected Friend Overridable Sub OnInput(ByVal e As GameEngineEventArgs)
        If _EnableRaisingEvents Then RaiseEvent Input(Me, e)
    End Sub

    ''' <summary>
    ''' This method is called by the GameEngine once per frame if a mousedown event was detected.
    ''' </summary>
    ''' <param name="e">A GameEngineEventArgs instance containing a reference to the GameEngine and GameTime.</param>
    ''' <remarks></remarks>
    Protected Friend Overridable Sub OnMouseDown(ByVal e As GameEngineEventArgs)
        If _EnableRaisingEvents Then RaiseEvent MouseDown(Me, e)
    End Sub

    ''' <summary>
    ''' This method is called by the GameEngine once per frame if a mouseup event was detected.
    ''' </summary>
    ''' <param name="e">A GameEngineEventArgs instance containing a reference to the GameEngine and GameTime.</param>
    ''' <remarks></remarks>
    Protected Friend Overridable Sub OnMouseUp(ByVal e As GameEngineEventArgs)
        If _EnableRaisingEvents Then RaiseEvent MouseUp(Me, e)
    End Sub

    ''' <summary>
    ''' This method is called by the GameEngine once per frame to allow this GameObject to execute its AI logic.
    ''' The default implementation updates all Sprites in this GameObject.
    ''' </summary>
    ''' <param name="e">A GameEngineEventArgs instance containing a reference to the GameEngine and GameTime.</param>
    ''' <remarks></remarks>
    Protected Friend Overridable Sub OnUpdate(ByVal e As GameEngineEventArgs)
        For Each item As Sprite In Sprites
            If TypeOf item Is AnimatedSprite Then
                DirectCast(item, AnimatedSprite).OnUpdate(e)
            End If
        Next
        If _EnableRaisingEvents Then RaiseEvent Update(Me, e)
    End Sub
End Class
